home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (c) 1992, 1993, 1994 Silicon Graphics, Inc.
- * All Rights Reserved.
- *
- * Permission to use, copy, modify, distribute, and sell this software and
- * its documentation for any purpose is hereby granted without fee, provided
- * that (i) the above copyright notices and this permission notice appear in
- * all copies of the software and related documentation, and (ii) the name of
- * Silicon Graphics may not be used in any advertising or
- * publicity relating to the software without the specific, prior written
- * permission of Silicon Graphics.
- *
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
- *
- * IN NO EVENT SHALL SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL,
- * INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY
- * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
- * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
- * OF THIS SOFTWARE.
- */
-
- /*
- * sonic.c - simple real time audio spatialization example with graphics
- *
- * This code originally appeared in the March/April 1993 Issue of the
- * Silicon Graphics support magazine "Pipeline", Volume 4, Number 2
- * accompanying the article titled "Adding Audio to an Existing Graphics
- * Application" as a coding example.
- *
- * A few extra comments have been added from the code in the article.
- * The functioning code is exactly the same as that of the article.
- */
-
- /* --------------------------------------------------------------------- */
-
- /* sonic.c - 11/92 Dan Fink */
- /* For use with the Digital Media Development Option */
- /* On Indigo family hardware platforms running IRIX 4.0.5F or better */
- /* Compile with -lgl -lsphere -laudio -laudiofile -lm */
-
- #include <limits.h>
- #include <sys/types.h>
- #include <sys/prctl.h>
- #include <sys/schedctl.h>
- #include <sys/lock.h>
- #include <stdio.h>
- #include <malloc.h>
- #include <gl/gl.h>
- #include <gl/device.h>
- #include <gl/sphere.h>
- #include <math.h>
- #include <audio.h>
- #include <audiofile.h>
-
- /* AUDIO DEFINES */
- #define BUFFERSIZE 4000 /* Size of output sample buffer. */
- #define PI 3.141592653 /* Our old friend from trig. */
- /* the following originated from the dmedia_tools.data.prosonus subsystem:
- #define BALLFILENAME "/usr/lib/sounds/prosonus/instr/metal_triad_power.E1.aiff"
- #define WALLFILENAME "/usr/lib/sounds/prosonus/musictags/slinky_slap.aiff"
- */
- #define BALLFILENAME "metal_triad_power.E1.aiff"
- #define WALLFILENAME "slinky_slap.aiff"
-
- /* OTHER DEFINES */
- #define SPHERERADIUS 1.0
- #define BOXSIZE 20.0
- #define NO_HIT 0
- #define JUST_HIT 1
- #define BEEN_HIT 2
-
- /* GRAPHICS PROTOTYPES */
- void sceneinit(long, long);
- Object makebox();
- void drawscene();
- void movesphere();
-
- /* AUDIO PROTOTYPES */
- void audioinit();
- long init_sound(float **, char*);
- void audioloop(void *);
- void process_oneshot_audio( float *, float, float, long);
- void process_continuous_audio( float *, float, float, long);
- void audioexit(int);
-
-
- /* AUDIO RELATED GLOBALS */
- float *ballbuf, *wallbuf; /* Sample buffers for ball and wall sounds */
- long ballframes,wallframes; /* Number of samples in each of these buffers */
- ALconfig audioconfig; /* Audio Library port configuration */
- ALport audioport; /* Audio Library audio port */
- AFfilesetup afsetup; /* Audio File Library file setup */
- AFfilehandle affh; /* Audio File Library file handle */
-
- /* OTHER GLOBALS */
- Object sphere,box; /* GL object identifiers for ball and box */
- float rx, ry; /* Angles of rotation for observer */
- float sphx,sphy,sphz; /* Coordinates of sphere */
- float movx,movy,movz; /* Vector for sphere motion */
- float hitx, hity, hitz; /* Coordinates of ball hitting wall */
- int hit; /* Status of ball hitting wall */
- int done; /* Indicates completion of graphics process */
- int audiodone; /* Indicates completion of audio process */
-
-
- /* GRAPHICS ROUTINES */
- void sceneinit(long xsize, long ysize) /* Iniitialize scene and GL set up */
- {
- static Matrix Identity = { 1, 0, 0, 0, 0, 1, 0, 0,
- 0, 0, 1, 0, 0, 0, 0, 1 };
-
- static float mat[] = { AMBIENT, .1, .1, .1,
- DIFFUSE, 0, .369, .165,
- SPECULAR, .5, .5, .5,
- SHININESS, 10,
- LMNULL }; /* Material of sphere */
-
- static float lm[] = { AMBIENT, .1, .1, .1,
- LOCALVIEWER, 1,
- LMNULL }; /* Lighting model for sphere */
-
- static float lt[] = { LCOLOR, 1, 1, 1,
- POSITION, 0, 0, 1, 0,
- LMNULL }; /* Light for sphere */
-
- /* Set up projection matrix and model-view stack */
- mmode(MVIEWING);
- loadmatrix(Identity);
- perspective(600, xsize/(float)ysize, .25, BOXSIZE*1.718);
-
- /* Initialize GL object for sphere using sphere library */
- sphere = genobj();
- sphmode (SPH_TESS, SPH_OCT);
- sphmode (SPH_DEPTH, 4);
- sphmode (SPH_PRIM, SPH_MESH);
- sphmode (SPH_HEMI, FALSE);
- sphmode (SPH_ORIENT, FALSE);
- sphobj(sphere);
-
- box = makebox(); /* Initialize GL object for walls (box) */
-
- /* Initialize lights */
- lmdef(DEFMATERIAL, 1, 0, mat);
- lmdef(DEFLIGHT, 1, 0, lt);
- lmdef(DEFLMODEL, 1, 0, lm);
- lmbind(MATERIAL, 1);
- lmbind(LMODEL, 1);
- lmbind(LIGHT0, 1);
- }
-
-
- Object makebox() /* Returns a GL object that defines walls (box) */
- {
- Object obj=genobj(); /* GL object to contain the box */
- static float v[8][3] = { /* Vertices for box */
- { -1.0, -1.0, -1.0 }, { 1.0, -1.0, -1.0 },
- { 1.0, 1.0, -1.0 }, { -1.0, 1.0, -1.0 },
- { -1.0, -1.0, 1.0 }, { 1.0, -1.0, 1.0 },
- { 1.0, 1.0, 1.0 }, { -1.0, 1.0, 1.0 } };
-
- makeobj(obj);
- bgntmesh(); /* tmesh for cube */
- cpack(0x000060); v3f(v[1]);
- cpack(0x000000); v3f(v[0]);
- cpack(0x006060); v3f(v[2]);
- cpack(0x006000); v3f(v[3]);
- cpack(0x606060); v3f(v[6]);
-
- swaptmesh();
- cpack(0x606000); v3f(v[7]);
- cpack(0x000000); v3f(v[0]);
- cpack(0x600000); v3f(v[4]);
-
- swaptmesh();
- cpack(0x600060); v3f(v[5]);
- cpack(0x000060); v3f(v[1]);
-
- swaptmesh();
- cpack(0x006060); v3f(v[2]);
- cpack(0x606060); v3f(v[6]);
- cpack(0x606000); v3f(v[7]);
-
- swaptmesh();
- cpack(0x600000); v3f(v[4]);
- cpack(0x600060); v3f(v[5]);
- endtmesh();
-
- pushmatrix(); /* Lines for cube outline */
- scale(0.99,0.99,0.99); /* Shrink lines just a tad, so they're visible */
- cpack(0xa0a0a0);
- bgnline();
- v3f(v[0]); v3f(v[1]);
- v3f(v[2]); v3f(v[3]);
- v3f(v[0]); v3f(v[4]);
- v3f(v[5]); v3f(v[6]);
- v3f(v[7]); v3f(v[4]);
- endline();
-
- bgnline();
- v3f(v[1]); v3f(v[5]);
- endline();
-
- bgnline();
- v3f(v[2]); v3f(v[6]);
- endline();
-
- bgnline();
- v3f(v[3]); v3f(v[7]);
- endline();
- popmatrix();
-
- closeobj();
- return(obj);
- }
-
-
- void drawscene() /* Draw current state of ball and walls */
- {
- pushmatrix();
- rot(ry, 'y'); /* Viewpoint orientation */
- rot(rx, 'x'); /* (We'll see these again in the audio routines) */
-
- backface(FALSE); /* Draw cube - don't need z buffer for this geometry */
- pushmatrix();
- scale(BOXSIZE,BOXSIZE,BOXSIZE);
- callobj(box);
- popmatrix();
-
- backface(TRUE); /* Draw sphere - don't need z buffer for this geometry*/
- pushmatrix();
- translate(sphx,sphy,sphz);
- scale(SPHERERADIUS,SPHERERADIUS,SPHERERADIUS);
- callobj(sphere);
- popmatrix();
-
- popmatrix();
- swapbuffers();
- }
-
-
- void movesphere() /* Add motion to sphere. Check for wall collisions */
- {
- sphx += movx; sphy += movy; sphz += movz;
- /* Move sphere in current direction */
-
- if (sphx > (BOXSIZE - SPHERERADIUS)) { /* Check for x bounce */
- movx = -movx;
- sphx = 2*(BOXSIZE - SPHERERADIUS) - sphx;
- hit = JUST_HIT;
- }
- else if (sphx < (SPHERERADIUS - BOXSIZE)) {
- movx = -movx;
- sphx = 2*(SPHERERADIUS - BOXSIZE) - sphx;
- hit = JUST_HIT;
- }
-
- if (sphy > (BOXSIZE - SPHERERADIUS)) { /* Check for y bounce */
- movy = -movy;
- sphy = 2*(BOXSIZE - SPHERERADIUS) - sphy;
- hit = JUST_HIT;
- }
- else if (sphy < (SPHERERADIUS - BOXSIZE)) {
- movy = -movy;
- sphy = 2*(SPHERERADIUS - BOXSIZE) - sphy;
- hit = JUST_HIT;
- }
-
- if (sphz > (BOXSIZE - SPHERERADIUS)) { /* Check for z bounce */
- movz = -movz;
- sphz = 2*(BOXSIZE - SPHERERADIUS) - sphz;
- hit = JUST_HIT;
- }
- else if (sphz < (SPHERERADIUS - BOXSIZE)) {
- movz = -movz;
- sphz = 2*(SPHERERADIUS - BOXSIZE) - sphz;
- hit = JUST_HIT;
- }
-
- if (hit == JUST_HIT) { /* Record where bounced */
- hitx = sphx; hity=sphy; hitz=sphz;
- }
- }
-
-
- /* AUDIO ROUTINES */
- void audioinit() /* Configure the audio port */
- {
- ballframes = init_sound( &ballbuf, BALLFILENAME); /* Get the ball waveform*/
- wallframes = init_sound( &wallbuf, WALLFILENAME); /* Get the wall waveform*/
-
- audioconfig = ALnewconfig(); /* Get a new audio configuration */
-
- ALsetsampfmt( audioconfig, AL_SAMPFMT_FLOAT); /* Sample format uses floats*/
- ALsetfloatmax(audioconfig, 1.0); /* Floats will vary from -1.0 to 1.0*/
-
- ALsetqueuesize(audioconfig,2*BUFFERSIZE);
- /* Set the sample queue size to be twice that of our sound buffer */
-
- ALsetchannels(audioconfig,AL_STEREO); /* Use stereo */
-
- audioport = ALopenport("sine","w",audioconfig); /* Open audio for writing */
- if (audioport == NULL) {
- printf("couldn't open audio port.\n");
- audioexit(-1);
- }
- }
-
-
- long init_sound(float **floatbuf, char *filename) /* Read audio file */
- {
- /* This routine reads the specified audio file, converts the samples
- to floating point, and return the length of the sample buffer.
- For simplicity sake, we're looking to read in 16 bit sounds in
- AF_SAMPFMT_TWOSCOMP format. Since our sound is eminating from a point
- source, we can only use mono information. For stereo sound files,
- only the left buffer is used. */
-
- long sampfmt; /* Sample format of the audio file */
- long numbits; /* Number of bits wide the samples are in audio file */
- long numchannels; /* Number of channels (1=mono,2=stereo) in audio file */
- long numframes; /* Number of sample frames in audio file */
- short *tempbuf; /* Temporary buffer to store integer samples */
- int i; /* Counter */
-
- if((afsetup = AFnewfilesetup()) == NULL) { /* Create new audio file setup */
- printf("Null file setup.\n");
- audioexit(-2);
- }
-
- if((affh = AFopenfile(filename,"r",afsetup)) == NULL) { /*Open audio file*/
- printf("Couldn't open audio file %s.\n",filename);
- audioexit(-3);
- }
-
- /* Determine validity of sample format */
- AFgetsampfmt(affh, AF_DEFAULT_TRACK, &sampfmt,&numbits);
- if(sampfmt != AF_SAMPFMT_TWOSCOMP) {
- printf("Strange audio file format in %s.\n",filename);
- audioexit(-4);
- }
-
- if (numbits != 16) { /* Check sample width */
- printf("Sample format isn't 16 bits in file %s.\n", filename);
- audioexit(-5);
- }
-
- numchannels = AFgetchannels(affh, AF_DEFAULT_TRACK);
- numframes = AFgetframecnt(affh, AF_DEFAULT_TRACK); /* Find buffer size */
- tempbuf = (short *)malloc(numframes*numchannels*sizeof(short));
-
- /* Read 16 bit samples into temp buffer before float conversion */
- if(AFreadframes(affh,AF_DEFAULT_TRACK,tempbuf,numframes) != numframes) {
- printf("Error reading audio file %s.\n",filename);
- audioexit(-6);
- }
-
- *floatbuf = (float *)malloc(numframes*sizeof(float));
- /* Create a buffer of floats - floats are much nicer than */
- /* integers for the amplitude calculations later on. */
-
- for(i=0; i<numframes; i++)
- (*floatbuf)[i] = (float)tempbuf[i*numchannels]/32767.0;
-
- /* Convert two's complement data into floating point data.*/
- /* Scale all samples to fit between -1.0 and 1.0. If the */
- /* file is stereo, then only use data from the left */
- /* channel. We are using a point source of sound here. */
-
- free(tempbuf); /* Free structures and close the audio file */
- AFclosefile(affh);
- affh = AF_NULL_FILEHANDLE;
- AFfreefilesetup(afsetup);
-
- return(numframes); /* Return number of frames read in */
- }
-
-
- void audioloop(void *dummy) /* Audio process main loop */
- {
- float distance,amplitude,balance; /* What we need to find out */
- float soundbuf[BUFFERSIZE]; /* Sample buffer for computations */
- float relx,rely,relz; /* Coordinates of ball relative to orientation */
- float radx,rady; /* Angle describing orientation in radians */
-
- if(schedctl(NDPRI,0,NDPHIMAX) == -1)
- printf("Couldn't run at high, non-degrading priority.\n");
- /* Make this process run at a high, non- */
- /* degrading priority. This help significantly */
- /* "smooth out" audio clicking caused by losing */
- /* the CPU to other processes. Works best when */
- /* the effective user id is the root user. */
- if(plock(PROCLOCK)!=0)
- printf("Couldn't lock process into memory.\n");
- /* Lock this process into memory - make it */
- /* immune to page swapping. Again, this is */
- /* another aid in preventing clicking which */
- /* works when the effective user id is the */
- /* root user. */
-
-
- audioinit(); /* Initialize audio */
- while(!done) { /* Keep going until gfx process says otherwise */
-
- /* Put sphere's coordinates through viewing transformation */
- /* Rot x (rx) * Rot y (rx) just like the graphics are doing */
- /* If translate() or scale() calls were used, they would need to */
- /* be represented here as well. Remember that the graphics pipe */
- /* pre-multiplies for matrix operations! */
-
- radx = PI*rx/180.0;
- rady = PI*ry/180.0;
-
- relx = sphx*cos(rady) + sphy*sin(radx)*sin(rady) +
- sphz*cos(radx)*sin(rady);
- rely = sphy*cos(radx) - sphz*sin(radx);
- relz = -sphx*sin(rady) + sphy*sin(radx)*cos(rady) +
- sphz*cos(radx)*cos(rady);
-
- distance = fsqrt( relx*relx + rely*rely + relz*relz );
-
- amplitude = 1.0 / (distance*distance + 1.0);
- /* Use inverse square proportion for amplitude */
-
- balance = acos( relx/distance) / PI;
- /* Compute the audio orientation of the sphere by taking the */
- /* angle of the projected vector from the observer to the object */
- /* the object onto the xy plane of the observer. Left-right */
- /* balance is easiest to achieve with a 2 speaker system such as */
- /* headphones, so we'll need to use the distance in the */
- /* x direction (relative to the observer's orientation) to find */
- /* left-right balance. With only 2 speakers top-bottom balance */
- /* is very difficult to simulate, so we'll ignore it here. */
-
- process_continuous_audio(soundbuf,amplitude,balance,BUFFERSIZE);
-
- if(hit != NO_HIT) { /* Don't bother with walls if we don't need to */
-
- /* Pass the coordinates of the hit through the same */
- /* transformations as above. */
-
- relx = hitx*cos(rady) + hity*sin(radx)*sin(rady) +
- hitz*cos(radx)*sin(rady);
- rely = hity*cos(radx) - hitz*sin(radx);
- relz = -hitx*sin(rady) + hity*sin(radx)*cos(rady) +
- hitz*cos(radx)*cos(rady);
-
- distance = fsqrt( relx*relx + rely*rely + relz*relz );
- balance = acos( relx/distance) / PI;
- amplitude = 1.0 / (distance*distance + 1.0);
-
- process_oneshot_audio(soundbuf,amplitude,balance,BUFFERSIZE);
- }
-
- while(ALgetfillable(audioport) < BUFFERSIZE)
- sginap(1); /* Wait til theres enough room to write the entire */
- /* copy of our sound buffer to the audio queue. */
-
- ALwritesamps(audioport,soundbuf,BUFFERSIZE);
- /* Output samples to audio port */
- }
-
- while(ALgetfilled(audioport) > 0);
- /* Wait till all the sound has been played before closing the port. */
-
- audioexit(0);
- }
-
-
- /* Process one-shot audio for bouncing off wall sound - time critical */
- void process_oneshot_audio(float *soundbuf, float amplitude,
- float balance, long buflen)
- {
- static long wallphase; /* Index into wall's sound buffer */
- long i; /* Counter */
- float rbalance = 1.0 - balance; /* Speeds up calculations */
-
- amplitude *= 5.0; /* The wall is usually pretty far away */
- /* boost up the volume for collisions */
-
- if(hit == JUST_HIT) /* If this is the first time around, the index */
- { /* into the sound buffer needs to be reinitialized */
- hit = BEEN_HIT; /* ialized, otherwise we may need to pick up */
- wallphase = 0; /* processing where we left off, since the entire */
- /* duration of sound may be longer than the time */
- } /* slice we need to process. */
-
- for(i=0; (i<buflen) && (wallphase < wallframes); wallphase++ ) {
-
- /* Since we know that this will be processed _after_ there is valid */
- /* audio data in the sample buffer, we'll perform the operation of */
- /* mixing the new audio data with the old audio data as the samples */
- /* are copied. In this case the mixing operation is a simple add. A */
- /* weighted average may be needed if the sum is likely to clip. */
-
- soundbuf[i++] += amplitude * balance * wallbuf[wallphase]; /* Left */
- soundbuf[i++] += amplitude * rbalance* wallbuf[wallphase]; /* Right */
-
- /* Note that a stereo soundbuffer is interleaved LRLRLR... */
- }
-
- if(wallphase == wallframes) /* If the sound buffer is exhausted, */
- hit = NO_HIT; /* we don't need to come back. */
- }
-
-
- /* Process audio sample loop for moving ball - time critical */
- void process_continuous_audio(float *soundbuf, float amplitude,
- float balance, long buflen)
- {
- static long ballphase = 0; /* Index into wall's sound buffer */
- long i; /* Counter */
- float rbalance = 1.0 - balance; /* Speeds up calculations */
-
- for(i=0; i<buflen; ballphase %= ballframes) {
-
- /* Since we know that this will be processed _before_ there is valid */
- /* audio data in the sample buffer, we'll can just copy the data into*/
- /* the buffer, if data were already in the sample buffer, mixing */
- /* would be necessary. Continuous looping is acheived by the modulo*/
- /* function above. */
-
- soundbuf[i++] = amplitude * balance * ballbuf[ballphase]; /* Left */
- soundbuf[i++] = amplitude * rbalance* ballbuf[ballphase++]; /* Right */
-
- /* Note that a stereo soundbuffer is interleaved LRLRLR... */
- }
- }
-
-
- void audioexit(int exitval) /* Gracefully exit the audio process */
- {
- if (wallbuf != NULL) free(wallbuf);
- if (ballbuf != NULL) free(ballbuf);
- if (audioconfig != NULL) ALfreeconfig(audioconfig);
- if (audioport != NULL) ALcloseport(audioport);
- if (affh != AF_NULL_FILEHANDLE) AFclosefile(affh);
- if (afsetup != NULL) AFfreefilesetup(afsetup);
-
- printf("audio closed down OK.\n");
- audiodone = TRUE;
- exit(exitval);
- }
-
-
- void main()
- {
- short val; /* Value returned from GL device */
- int audio_pid; /* Process id of child audio process */
- int moveball = TRUE; /* Flag for ball motion - controlled by LEFTMOUSE */
- long xorigin, yorigin; /* Origin of GL window */
- long xsize, ysize; /* Size of GL window */
-
- /* Initialize globals */
- done = audiodone = FALSE;
- rx = ry = 0.0;
- sphx = 1.0; sphy = 2.0; sphz = 3.0;
- movx = 0.1; movy = 0.2; movz = 0.1;
- hit = NO_HIT;
-
- foreground();
- audio_pid = sproc(audioloop,PR_SALL); /* Fork audio process */
- if (audio_pid == -1) {
- fprintf(stderr,"Couldn't create audio process.\n");
- exit(-1);
- }
-
- winopen("sonic"); /* Initialize window */
- RGBmode();
- doublebuffer();
- gconfig();
-
- getorigin(&xorigin, &yorigin); /* Get window info */
- getsize(&xsize, &ysize);
-
- qdevice(ESCKEY); /* Queue devices */
- qdevice(WINQUIT);
- qdevice(REDRAW);
- qdevice(MOUSEX);
- qdevice(MOUSEY);
- qdevice(LEFTMOUSE);
- sceneinit(xsize,ysize);
-
- while (!done && !audiodone) { /* Event loop */
- while(qtest()) {
- switch(qread(&val)) {
-
- case WINQUIT:
- done = TRUE;
- break;
-
- case ESCKEY:
- done = !val; /* Quit on ESC key up */
- break;
-
- case REDRAW:
- getorigin(&xorigin, &yorigin);
- getsize(&xsize, &ysize);
- reshapeviewport();
- break;
-
- case MOUSEX:
- ry=300*(2.0*(val-xorigin)/xsize-1.0);
- break;
-
- case MOUSEY:
- rx=300*(2.0*(val-yorigin)/ysize-1.0);
- break;
-
- case LEFTMOUSE: /* Stop/start ball motion */
- if(val) {
- moveball = !moveball;
- hit = JUST_HIT;
- hitx = sphx; hity=sphy; hitz=sphz;
- }
- break;
- }
- }
- if (moveball) movesphere();
- drawscene();
- }
- fprintf(stderr,"out of gfx event loop.\n");
- while (!audiodone)
- sginap(1); /* Wait for audio to shut down properly */
-
- delobj(box);
- delobj(sphere);
- exit(0);
- }
-